<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover"><title>SpringCloudPractice | 静待花开</title><meta name="author" content="brad 随风起舞"><meta name="copyright" content="brad 随风起舞"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="springcloud使用Netflix OSS微服务结构概要在微服务结构中Netflix是开创者.他首次成功实现了大范围的微服务架构.它使得微服务变得更流行并且通过netflix OOS(open source software center)开源他们大部分的微服务工具. 根据Netflix博客的说法,当Netflix开发其平台时,他们使用Apache Cassandra进行数据储存,这是apa">
<meta property="og:type" content="article">
<meta property="og:title" content="SpringCloudPractice">
<meta property="og:url" content="https://zhangbo4881820.gitee.io/myblog/posts/55288/index.html">
<meta property="og:site_name" content="静待花开">
<meta property="og:description" content="springcloud使用Netflix OSS微服务结构概要在微服务结构中Netflix是开创者.他首次成功实现了大范围的微服务架构.它使得微服务变得更流行并且通过netflix OOS(open source software center)开源他们大部分的微服务工具. 根据Netflix博客的说法,当Netflix开发其平台时,他们使用Apache Cassandra进行数据储存,这是apa">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png">
<meta property="article:published_time" content="2019-11-29T03:32:24.000Z">
<meta property="article:modified_time" content="2023-09-07T09:44:01.901Z">
<meta property="article:author" content="brad 随风起舞">
<meta property="article:tag" content="springCloud">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png"><link rel="shortcut icon" href="/myblog/img/favicon.png"><link rel="canonical" href="https://zhangbo4881820.gitee.io/myblog/posts/55288/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/myblog/css/index.css"><link rel="stylesheet" href="/myblog/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="/myblog/css/fancybox.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = {
  root: '/myblog/',
  algolia: undefined,
  localSearch: {"path":"/myblog/search.xml","preload":false,"top_n_per_article":1,"unescape":false,"languages":{"hits_empty":"找不到您查询的内容：${query}","hits_stats":"共找到 ${hits} 篇文章"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  dateSuffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: false,
  },
  autoDarkmode: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'SpringCloudPractice',
  isPost: true,
  isHome: false,
  isHighlightShrink: true,
  isToc: true,
  postUpdate: '2023-09-07 17:44:01'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 7.0.0-rc2"><link rel="alternate" href="/myblog/atom.xml" title="静待花开" type="application/atom+xml">
</head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/myblog/archives/"><div class="headline">文章</div><div class="length-num">57</div></a><a href="/myblog/tags/"><div class="headline">标签</div><div class="length-num">20</div></a><a href="/myblog/categories/"><div class="headline">分类</div><div class="length-num">25</div></a></div><hr class="custom-hr"/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/myblog/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 分享</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/myblog/Gallery/"><i class="fa-fw fas fa-images"></i><span> 照片</span></a></li><li><a class="site-page child" href="/myblog/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/myblog/movies/"><i class="fa-fw fas fa-video"></i><span> 视频</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/myblog/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog-info"><a href="/myblog/" title="静待花开"><span class="site-name">静待花开</span></a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search" href="javascript:void(0);"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/myblog/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 分享</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/myblog/Gallery/"><i class="fa-fw fas fa-images"></i><span> 照片</span></a></li><li><a class="site-page child" href="/myblog/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/myblog/movies/"><i class="fa-fw fas fa-video"></i><span> 视频</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/myblog/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/myblog/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout" id="content-inner"><div id="post"><div id="post-info"><h1 class="post-title">SpringCloudPractice</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2019-11-29T03:32:24.000Z" title="发表于 2019-11-29 11:32:24">2019-11-29</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-09-07T09:44:01.901Z" title="更新于 2023-09-07 17:44:01">2023-09-07</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/myblog/categories/springCloud/">springCloud</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="SpringCloudPractice"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div><article class="post-content" id="article-container"><h1 id="springcloud"><a href="#springcloud" class="headerlink" title="springcloud"></a>springcloud</h1><h2 id="使用Netflix-OSS微服务结构概要"><a href="#使用Netflix-OSS微服务结构概要" class="headerlink" title="使用Netflix OSS微服务结构概要"></a>使用Netflix OSS微服务结构概要</h2><p>在微服务结构中Netflix是开创者.他首次成功实现了大范围的微服务架构.它使得微服务变得更流行并且通过netflix OOS(open source software center)开源他们大部分的微服务工具.</p>
<p>根据Netflix博客的说法,当Netflix开发其平台时,他们使用Apache Cassandra进行数据储存,这是apache的开源工具.他们开始通过修复和优化扩展为Cassandra做出贡献。这使Netflix看到发布名为OSS的Netflix项目的好处。</p>
<p>Spring抓住机会整合许多Netflix OSS 项目,例如Zuul,Ribbon,Hystrix,Eureka server,Turbine等整合进spring Cloud.这就是Spring Cloud提供现成的平台来开发可投入生产的微服务的原因之一。现在看看如下的结构图:</p>
<p><img src="/myblog/image/microservice-architecture-diagram.png"></p>
<p>正如你所见,对于每个微服务的实践,都有一个Netflix工具关联它.通过下列表进行理解:</p>
<ul>
<li><p>**Edge server:**我们使用Netflix Zuul 服务作为一个边缘服务.</p>
</li>
<li><p>**Load balancing:**Netflix Ribbon被用于负载均衡.</p>
</li>
<li><p>**Circuit breaker:**Netflix Hystrix被用于断路器和维护系统运行</p>
</li>
<li><p>**Service discovery and registration:**Netflix Eureka server 被用于服务发现与注册.</p>
</li>
<li><p><strong>Monitoring dashboard:</strong> Hystrix dashboard和Netflix Turbine被用于监控.</p>
</li>
</ul>
<h2 id="服务注册与发现"><a href="#服务注册与发现" class="headerlink" title="服务注册与发现"></a>服务注册与发现</h2><p><strong>Netflix Eureka</strong>服务用于服务的注册与发现,在接下来实践它不仅满足服务注册和发现,还能使用<strong>Ribbon</strong>提供负载均衡</p>
<h2 id="边缘服务-Edge-servers"><a href="#边缘服务-Edge-servers" class="headerlink" title="边缘服务(Edge servers)"></a>边缘服务(Edge servers)</h2><p>一个边缘服务给外界提供一个单点访问去和你的系统交互.你所有的APIs和前端只能使用这个服务访问.因此这中服务也被称为网关或者代理服务.可以配置路由去访问不同的微服务或者前端应用.在我们实践项目中,我们使用<strong>Netflix Zuul</strong>服务作为边缘服务</p>
<h2 id="负载均衡"><a href="#负载均衡" class="headerlink" title="负载均衡"></a>负载均衡</h2><p><strong>Netflix Ribbon</strong>服务用于负载均衡.它和Zuul,Eureka集成在一起,为内外调用提供负载均衡.</p>
<p>需要以最大速度和容量利用率的方式为请求提供负载平衡，并确保没有服务器因请求而过载。如果服务器发生故障，负载平衡器还会将请求重定向到其余主机服务器。在微服务架构中，微服务可以服务内部或外部请求。基于此，我们可以有两种类型的负载平衡-客户端和服务器端负载平衡。</p>
<h3 id="服务端负载均衡"><a href="#服务端负载均衡" class="headerlink" title="服务端负载均衡"></a><strong>服务端负载均衡</strong></h3><p>在讨论服务端负载均衡之前,先来说说路由.定义路由机制对从微服务结构视图点到我们的应用是非常重要的.例如,&#x2F;(root)能够映射到我们的UI应用,&#x2F;restaurantapi和&#x2F;userapi能够映射到相应的Restaurant和User服务.边缘服务也能够执行带有负载均衡的路由.</p>
<p>我们使用Netflix Zuul服务作为我们的边缘服务.Zuul是一个基于JVM的路由器和服务端的负载均衡器.Zuul支持任何JVM语言去写规则,过滤器,构建对JAVA和Groovy的支持.</p>
<p>默认情况下，Netflix Zuul具有发现客户端（Eureka客户端）支持。Zuul还利用Ribbon和Eureka进行负载平衡</p>
<p>外部世界(客户端)调用边缘服务,使用在<em>application.yml</em>定义好的路由去调用内部服务和提供响应.可用把它想象成代理服务,为内部网络充当网关的职责,并且可用调用定义好的和配置了路由的内部服务.</p>
<p>通常,推荐单边缘服务服务于所有请求.然而，一些公司为每个客户端使用单个边缘服务器进行扩展。例如，Netflix为每种设备类型使用专用的边缘服务器.</p>
<p><strong>实现边缘服务步骤:</strong></p>
<ol>
<li><p>在pom.xml文件中定义Zuul服务依赖</p>
<pre><code> &lt;dependency&gt; 
     &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt; 
     &lt;artifactId&gt;spring-cloud-starter-zuul&lt;/artifactId&gt; 
 &lt;/dependency&gt;
</code></pre>
</li>
<li><p>在应用程序类中使用@EnableZuulProxy注解。它还在内部使用@EnableDiscoveryClient注解；因此，它也会自动注册到Eureka服务器。您可以在“客户端负载平衡”部分的图中找到注册的Zuul服务器。</p>
</li>
<li><p>在 <em>application.yml</em>中更新zuul的配置</p>
<ul>
<li><p>zuul:ignoredServices:这将跳过自动添加服务我们可以在这里定义服务ID模式。*表示我们将忽略所有服务。</p>
</li>
<li><p>Zuul.routes:这包含一个定义在URI模式的路径属性.这里,<code>/restaurantapi</code>使用<code>serviceId</code>属性映射到<code>restaurant-service</code>; <code>serviceId</code>属性表示Eureka Server中的服务名.如果Eureka服务没有使用,你可以使用URL替换服务名.<br> 同时使用的<code>stripPrefix</code>属性去除前缀(&#x2F;restaurantapi),当调用<code>/restaurantapi/v1/restaurants/1</code>会转换成<code>/v1/restaurants/1</code></p>
<pre><code>  zuul:
      ignoredServices: &quot;*&quot;
      routes:
          restaurantapi:
              path: /restaurantapi/**
              serviceId: restaurant-service
              stripPrefix: true
  server: 
      port: 8765 
  # Discovery Server Access 
  eureka: 
      instance: 
          leaseRenewalIntervalInSeconds: 3 
          metadataMap: 
          instanceId: $&#123;vcap.application.instance_id:$&#123;spring.application.name&#125;:$&#123;spring.application.instance_id:$&#123;random.value&#125;&#125;&#125;
          serviceUrl: 
              defaultZone: http://localhost:8761/eureka/
              fetchRegistry: false
</code></pre>
</li>
</ul>
</li>
</ol>
<p>请注意，Eureka应用程序仅为每个主机注册任何服务的单个实例。您需要对metamapMap.instanceid使用以下值，以在一个主机上注册同一应用程序的多个实例，以使负载平衡起作用.<br><code>$&#123;spring.application.name&#125;:$&#123;vcap.application.instance_id:     $&#123;spring.application.instance_id:$&#123;random.value&#125;&#125;&#125; </code></p>
<p>首先使用部署在端口11169的restaurant服务调用端点:</p>
<p><img src="/myblog/image/restaurants-application-call.png"></p>
<p>接下来,我们使用部署在8765上的边缘服务来调用接口.可用看到结果一样.</p>
<p><img src="/myblog/image/restaurants-application-call-edge.png"></p>
<h3 id="客户端负载均衡"><a href="#客户端负载均衡" class="headerlink" title="客户端负载均衡"></a><strong>客户端负载均衡</strong></h3><p>微服务需要进程间通信，以便服务可以相互通信。Spring Cloud使用Netflix Ribbon，这是一个客户端负载平衡器，担当着至关重要的角色，可以处理HTTP和TCP。功能区已启用云，并提供内置的故障恢复能力。功能区还允许您使用多个可插入的负载平衡规则。它将客户端与负载平衡器集成在一起。</p>
<p>默认的,在springCloud中Ribbon是和Eureka服务整合在一起的.他们提供如下特性:</p>
<ul>
<li><p>当Eureka服务启动的时候,你无需对远程服务器URL进行硬编码即可发现.这是一个显着的优势，尽管如果需要，您仍然可以在application.yml文件中使用已配置的服务器列表.</p>
</li>
<li><p>服务器列表从Eureka服务器填充。Eureka服务器使用DiscoveryEnabledNIWSServerList接口覆盖RibbonServerList接口</p>
</li>
<li><p>查找服务器是否已启动的请求委托给Eureka。使用DiscoveryEnabledNIWSServerList接口代替Ribbon的IPing。</p>
</li>
</ul>
<p>Spring Cloud中有使用Ribbon的不同客户端，例如RestTemplate或FeignClient。这些客户端允许微服务相互通信。使用Eureka服务器时，客户端使用实例ID代替主机名和端口来对服务实例进行HTTP调用。客户端将服务ID传递给Ribbon，然后使用负载均衡器从Eureka服务器中选择实例</p>
<p>如果在Eureka服务中有多个实例可用,Ribbon仅为请求挑选一个服务,基于负载均衡算法.</p>
<p>我们可以使用DiscoveryClient在Eureka服务器中查找所有可用的服务实例，如以下代码所示。DiscoveryClientSample类的getInstances（）方法返回Eureka服务器中可用的所有本地服务实例。</p>
<pre><code>@Component
class DiscoveryClientSample implements CommandLineRunner &#123;

@Autowired
private DiscoveryClient discoveryClient;

@Override
public void run(String... strings) throws Exception &#123;
    System.out.println(discoveryClient.description());
    discoveryClient.getInstances(&quot;restaurant-service&quot;).forEach((ServiceInstance serviceInstance) -&gt; &#123;
        System.out.println(&quot;Instance --&gt; &quot; + serviceInstance.getServiceId()
                + &quot;\nServer: &quot; + serviceInstance.getHost() + &quot;:&quot; + serviceInstance.getPort()
                + &quot;\nURI: &quot; + serviceInstance.getUri() + &quot;\n\n\n&quot;);
    &#125;);
&#125;
&#125;
</code></pre>
<p>先启动那个RESTAURANT应用实例.然后启动边缘服务,可用看到如下打印结果:</p>
<blockquote>
<p>Composite Discovery Client<br> Instance –&gt; RESTAURANT-SERVICE<br> Server: localhost:8872<br> URI: <a href="http://localhost:8872/">http://localhost:8872</a> <br/><br> Instance –&gt; RESTAURANT-SERVICE<br> Server: localhost:8871<br> URI: <a href="http://localhost:8871/">http://localhost:8871</a></p>
</blockquote>
<p> 以下示例展示了如何使用这些客户端。您可以看到在两个客户端中，都使用服务名称restaurant-service代替了服务主机名和端口。这些客户调用&#x2F;v1&#x2F;restaurants以获取包含名称查询参数中给出的名称的餐厅列表</p>
<pre><code> @FeignClient(&quot;restaurant-service&quot;)
interface RestaurantClient &#123;
    @RequestMapping(method = RequestMethod.GET, value = &quot;/v1/restaurants&quot;)
    Collection&lt;Restaurant&gt; getRestaurants(@RequestParam(&quot;name&quot;) String name);
&#125;
@Component
class FeignSample implements CommandLineRunner &#123;
    @Autowired
    private RestaurantClient restaurantClient;
    @Override
    	public void run(String... strings) throws Exception &#123;
        this.restaurantClient.getRestaurants(&quot;o&quot;).forEach((Restaurant restaurant) -&gt; &#123;
            System.out.println(&quot;\n\n\n[ &quot; + restaurant.getId() + &quot; &quot; + restaurant.getName() + &quot;]&quot;);
        &#125;);
    &#125;
&#125;

@Component
class RestTemplateExample implements CommandLineRunner &#123;

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public void run(String... strings) throws Exception &#123;
        System.out.println(&quot;\n\n\n start RestTemplate client...&quot;);
        ResponseEntity&lt;Collection&lt;Restaurant&gt;&gt; exchange
                = this.restTemplate.exchange(
                    &quot;http://restaurant-service/v1/restaurants?name=o&quot;,
                    HttpMethod.GET,
                    null,
                    new ParameterizedTypeReference&lt;Collection&lt;Restaurant&gt;&gt;() &#123;&#125;,
                    (Object) &quot;restaurants&quot;);
        exchange.getBody().forEach((Restaurant restaurant) -&gt; &#123;
            System.out.println(&quot;\n\n\n[ &quot; + restaurant.getId() + &quot; &quot; + restaurant.getName() + &quot;]&quot;);
        &#125;);
    &#125;
&#125;
</code></pre>
<h2 id="断路器-Circuit-breakers"><a href="#断路器-Circuit-breakers" class="headerlink" title="断路器(Circuit breakers)"></a>断路器(Circuit breakers)</h2><p>一个错误或一个服务断掉不应该阻止整个系统的工作.一个服务的重复错误或API的错误应该被适当的处理.断路器就是提供这些特性.<strong>Netflix Hystrix</strong>被用作断路器和维持系统工作.</p>
<p>一般而言，断路器是一种自动装置，作为安全措施，用于停止电路中的电流流动。</p>
<p>相同的概念用于微服务开发，称为断路器设计模式。它跟踪诸如Eureka服务器之类的外部服务，诸如restaurantservice之类的API服务的可用性，并阻止服务使用者对任何不可用的服务执行任何操作。</p>
<p>微服务体系结构的另一个重要方面是一种安全措施（故障安全机制），当服务不响应服务使用者发出的呼叫时,就要使用断路器。</p>
<p>我们使用Netflix Hystrix 作为一个断路器.发生故障时（例如，由于通信错误或超时），它将在服务使用者中调用内部回退方法。<br>它在其服务使用者中嵌入执行.</p>
<p>当服务无法重复响应时，Hystrix会断开电路并进行快速故障切换，直到该服务再次可用为止。当对特定服务的呼叫达到某个阈值时（默认阈值是五秒内出现20次故障），电路将断开并且不会进行呼叫。您一定想知道，如果Hystrix断开电路，那么它如何知道该服务可用？<br>它可用允许某些请求特例来调用该服务</p>
<h3 id="使用Hystrix的回退方法"><a href="#使用Hystrix的回退方法" class="headerlink" title="使用Hystrix的回退方法"></a>使用Hystrix的回退方法</h3><p>实施回退方法有五个步骤。为此，我们将以创建其他服务的相同方式创建另一个服务api-service。api-service服务将使用其他服务，例如restaurant-service等，并将在边缘服务器中配置为将应用API公开给外部使用。</p>
<ol>
<li><p>启用断路器:使用其他服务的微服务的主类应使用@EnableCircuitBreaker进行注释。</p>
<pre><code> @SpringBootApplication 
 @EnableCircuitBreaker 
 @ComponentScan(&#123;&quot;com.packtpub.mmj.user.service&quot;, &quot;com.packtpub.mmj.common&quot;&#125;) public class ApiApp &#123;
</code></pre>
</li>
<li><p>配置回退方法:@HystrixCommand是被用于配置回退方法的注解</p>
<pre><code> @RequestMapping(&quot;/restaurants/&#123;restaurant-id&#125;&quot;) 
 @HystrixCommand(fallbackMethod = &quot;defaultRestaurant&quot;) 
 public ResponseEntity&lt;Restaurant&gt; getRestaurant( @PathVariable(&quot;restaurant-id&quot;) int restaurantId) &#123; 	MDC.put(&quot;restaurantId&quot;, restaurantId); 
     String url = &quot;http://restaurant-service/v1/restaurants/&quot; + restaurantId; 
     LOG.debug(&quot;GetRestaurant from URL: &#123;&#125;&quot;, url); 
     ResponseEntity&lt;Restaurant&gt; result = restTemplate.getForEntity(url, Restaurant.class); LOG.info(&quot;GetRestaurant http-status: &#123;&#125;&quot;, result.getStatusCode()); LOG.debug(&quot;GetRestaurant body: &#123;&#125;&quot;, result.getBody()); 
     return serviceHelper.createOkResponse(result.getBody()); 
 &#125;
</code></pre>
</li>
<li><p>定义回退方法:</p>
<pre><code> public ResponseEntity&lt;Restaurant&gt; defaultRestaurant( @PathVariable int restaurantId) &#123; 
     return serviceHelper.createResponse(null, HttpStatus.BAD_GATEWAY); 
 &#125;
</code></pre>
</li>
<li><p>maven依赖:</p>
<pre><code> &lt;dependency&gt; 
     &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt; 
     &lt;artifactId&gt;spring-cloud-starter-hystrix&lt;/artifactId&gt; 
 &lt;/dependency&gt;
</code></pre>
</li>
<li><p>在yml配置文件中配置Hystrix:</p>
<pre><code> hystrix: 
     threadpool: 
         default: 
             # Maximum number of concurrent requests when using thread pools (Default: 10) 
             coreSize: 100 
             # Maximum LinkedBlockingQueue size - -1 for using SynchronousQueue (Default: -1) maxQueueSize: -1 
             # Queue size rejection threshold (Default: 5) 
             queueSizeRejectionThreshold: 5 
     command: 
         default: 
             circuitBreaker: 
                 sleepWindowInMilliseconds: 30000 
                 requestVolumeThreshold: 2 
             execution: 
                 isolation: 
                 # strategy: SEMAPHORE, no thread pool but timeout handling stops to work 
                 strategy: THREAD 
                 thread: 
                     timeoutInMilliseconds: 6000
</code></pre>
</li>
</ol>
<p>这些步骤应足以使服务调用失效保护，并向服务使用者返回更适当的响应。</p>
<h2 id="监控器-Monitoring"><a href="#监控器-Monitoring" class="headerlink" title="监控器(Monitoring)"></a>监控器(Monitoring)</h2><p><strong>Hystrix dashboard</strong>和<strong>Netflix Turbine</strong>用于微服务的监控.它提供一个仪表盘去检查运行中的微服务.</p>
<p>Hystrix提供一个很好的断路器图形化WEB UI仪表盘.</p>
<p>Netflix Turbine是一个Web应用程序，它连接到群集中的Hystrix应用程序的实例并聚合信息，并实时进行（每0.5秒更新一次）。涡轮使用称为涡轮流的流提供信息.</p>
<p>如果将Hystrix与Netflix Turbine结合使用，则可以从Hystrix仪表板上的Eureka服务器获取所有信息。这为您提供了有关断路器的所有信息的横向视图</p>
<p>要将Turbine与Hystrix一起使用，只需在前面的屏幕快照中显示的第一个文本框中输入Turbine URL http：&#x2F;&#x2F; localhost：8989 &#x2F; turbine.stream（在application.yml中为Turbine服务器配置了端口8989），然后单击监视流</p>
<p>Netflix Hystrix和Turbine使用RabbitMQ（开源消息队列软件）。RabbitMQ使用高级消息队列协议（AMQP）。它是一种软件，可以在其中定义队列，并且连接的应用程序可以使用该队列来交换消息。一条消息可以包含任何种类的信息。一条消息可以存储在RabbitMQ队列中，直到接收方应用程序连接并使用该消息（将消息从队列中取出）为止。</p>
<p>Hystrix使用RabbitMQ将指标数据提要发送到Turbine</p>
<p><em>在配置Hystrix和turbine之前先下载安装好RabbutMQ应用,Hystrix和turbine之间使用RabbutMQ交互信息</em></p>
<h3 id="创建Hystrix-dashboard服务"><a href="#创建Hystrix-dashboard服务" class="headerlink" title="创建Hystrix dashboard服务"></a><strong>创建Hystrix dashboard服务</strong></h3><p>我们将以与创建其他服务相同的方式在IDE中为Hystrix仪表板创建另一个项目。在这个新项目中，我们将为Hystrix服务器添加新的Maven依赖关系，dashboard-server。在Spring Cloud中，配置和使用Hystrix仪表板非常简单。</p>
<p>当你运行Hystrix仪表板项目,访问项目地址,就能看到默认的仪表盘.步骤如下:</p>
<ol>
<li><p>在pom.xml中定义依赖</p>
<pre><code> &lt;dependency&gt; 
     &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt; 
     &lt;artifactId&gt;spring-cloud-starter-hystrix-dashboard&lt;/artifactId&gt; 
 &lt;/dependency&gt;
</code></pre>
</li>
<li><p>@EnableHystrixDashboard 在主方法上使用这个注解就能使用全部仪表盘功能了.同时使用@controller注解来转发来自根URI的请求到Hystrix仪表盘UI地址(&#x2F;hystrix).如下:</p>
<pre><code> @SpringBootApplication 
 @Controller 
 @EnableHystrixDashboard 
 public class DashboardApp extends SpringBootServletInitializer &#123; 
     @RequestMapping(&quot;/&quot;) 
     public String home() &#123; 
         return &quot;forward:/hystrix&quot;; 
     &#125; 
     @Override 
     protected SpringApplicationBuilder configure(SpringApplicationBuilder application) &#123; 
         return application.sources(DashboardApp.class).web(true); 
         &#125; 
     public static void main(String[] args) &#123; 
         SpringApplication.run(DashboardApp.class, args); 
         &#125; 
     &#125;
</code></pre>
</li>
<li><p>配置仪表盘服务</p>
<pre><code> spring: 
     application: 
         name: dashboard-server 
 endpoints: 
     restart: 
         enabled: true 
     shutdown: 
         enabled: true 
 server: 
     port: 7979
 eureka: 
     instance: 
         leaseRenewalIntervalInSeconds: 3 
         metadataMap: 
         instanceId: $&#123;vcap.application.instance_id:$&#123;spring.application.name&#125;:$&#123;spring.application.ins
 client: 
 # Default values comes from org.springframework.cloud.netflix.eurek.EurekaClientConfigBean registryFetchIntervalSeconds: 5 
 instanceInfoReplicationIntervalSeconds: 5 
 initialInstanceInfoReplicationIntervalSeconds: 5 
 serviceUrl: 
     defaultZone: http://localhost:8761/eureka/ 
 fetchRegistry: false

 logging: 
     level: 
         ROOT: WARN 
         org.springframework.web: WARN
</code></pre>
</li>
</ol>
<h3 id="创建Netflix-Turbine服务"><a href="#创建Netflix-Turbine服务" class="headerlink" title="创建Netflix Turbine服务"></a><strong>创建Netflix Turbine服务</strong></h3><p>Turbine将所有&#x2F;hystrix.stream端点聚合到一个组合的&#x2F;turbine.stream中，以便在Hystrix仪表板中使用，这更加有用，因为它允许在单个仪表板中查看系统的整体运行状况，而不是使用&#x2F;hystrix监视单个服务.步骤如下:</p>
<ol>
<li><p>定义maven依赖</p>
<pre><code> &lt;dependency&gt; 
     &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt; 
     &lt;artifactId&gt;spring-cloud-starter-turbine-stream&lt;/artifactId&gt; 
 &lt;/dependency&gt; 
 &lt;dependency&gt; 
     &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt; 
     &lt;artifactId&gt;spring-cloud-starter-stream-rabbit&lt;/artifactId&gt; 
 &lt;/dependency&gt; 
 &lt;dependency&gt; 
     &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; 
     &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt; 
 &lt;/dependency&gt;
</code></pre>
</li>
<li><p>@EnableTurbineStream 在主类上使用这个注解.同时我们也顶一个bean用来返回RabbitMQ的连接工厂(ConnectionFactory)</p>
<pre><code> @SpringBootApplication 
 @EnableTurbineStream 
 @EnableEurekaClient 
 public class TurbineApp &#123; 

     private static final Logger LOG = LoggerFactory.getLogger(TurbineApp.class); @Value(&quot;$&#123;app.rabbitmq.host:localhost&#125;&quot;) 
     String rabbitMQHost; 
     @Bean 
     public ConnectionFactory connectionFactory() &#123; 
         LOG.info(&quot;Creating RabbitMQHost ConnectionFactory for host: &#123;&#125;&quot;, rabbitMQHost); CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(rabbitMQHost); 
         return cachingConnectionFactory; 
     &#125; 
     public static void main(String[] args) &#123; 
         SpringApplication.run(TurbineApp.class, args); 
         &#125; 
     &#125;
</code></pre>
</li>
<li><p>配置文件</p>
<ul>
<li><p><small>server:port:</small>这是用于Turbine HTTP的主端口</p>
</li>
<li><p><small>management:port:</small>这是Turbine actuator端点的端口</p>
<pre><code>  spring: 
      application: 
          name: turbine-server
  server:
      port: 8989
  management:
      port: 8991
  turbine: 
      aggregator: 
          clusterConfig: USER-SERVICE,RESTAURANT-SERVICE 
      appConfig: user-service,restaurant-service
  eureka:
      instance:
          leaseRenewalIntervalInSeconds: 3
          #解决无法注册到eureka服务的解决.因为spring-cloud-turbine-stream包会依赖netty容器并将server.port设为-1,从而关闭servlet
          #但是服务端口会显示-1是不正确的,这个值必须和server.port值一致
          non-secure-port: 8989
          metadataMap:
              instanceId: $&#123;vcap.application.instance_id:$&#123;spring.application.name&#125;:$&#123;spring.application.instance_id:$&#123;random.value&#125;&#125;&#125;
              #自定义ID
          instance-id: $&#123;spring.cloud.client.hostname&#125;:$&#123;spring.application.name&#125;:$&#123;eureka.instance.non-secure-port&#125;
      client:
          registryFetchIntervalSeconds: 5
          instanceInfoReplicationIntervalSeconds: 5
          initialInstanceInfoReplicationIntervalSeconds: 5
          serviceUrl:
              defaultZone: $&#123;vcap.services.$&#123;PREFIX:&#125;eureka.credentials.uri:http://user:password@localhost:8761&#125;/eureka/
          fetchRegistry: true
  logging: 
      level: 
          root: INFO 
          com.netflix.discovery: &#39;OFF&#39; 
          org.springframework.integration: DEBUG
</code></pre>
</li>
</ul>
</li>
<li><p>启动zuul服务之前,把所有服务启动.</p>
<pre><code> java -jar eureka-server/target/eureka-server.jar 
 java -jar turbine-server/target/turbine-server.jar 
 java -jar dashboard-server/target/dashboard-server.jar 
 java -jar restaurant-service/target/restaurant-service.jar 
 java -jar user-service/target/user-service.jar 
 java -jar booking-service/target/booking-service.jar 
 java -jar api-service/target/api-service.jar
</code></pre>
</li>
</ol>
<p>最后启动zuul<br>        java -jar zuul-server&#x2F;target&#x2F;zuul-server.jar</p>
<h2 id="使用docker容器部署微服务"><a href="#使用docker容器部署微服务" class="headerlink" title="使用docker容器部署微服务"></a>使用docker容器部署微服务</h2><p>Docker容器提供了一个轻量级的运行时环境，该环境由虚拟机的核心功能和操作系统的隔离服务（称为Docker映像）组成。Docker使微服务的打包和执行更加轻松和顺畅。每个操作系统可以有多个Docker，每个Docker可以运行单个应用程序。您可以安装VirtualBox或类似工具（例如Docker Toolbox）以使其适合您。</p>
<p>Docker安装页面提供了有关它的更多详细信息，并让您知道如何做。可用基于你的系统,选择合适的安装.通过访问<a href="https://docs.docker.com/install/">docker官网</a></p>
<p>默认计算机创建时具有2 GB的内存。我们将重新创建具有4 GB内存的Docker计算机</p>
<h3 id="使用maven插件构建镜像"><a href="#使用maven插件构建镜像" class="headerlink" title="使用maven插件构建镜像"></a>使用maven插件构建镜像</h3><p>常用三款插件如下:</p>
<ul>
<li><p>(<a href="https://github.com/rhuss/docker-maven-plugin">https://github.com/rhuss/docker-maven-plugin</a>)</p>
</li>
<li><p>(<a href="https://github.com/alexec/docker-maven-plugin">https://github.com/alexec/docker-maven-plugin</a>)</p>
</li>
<li><p>(<a href="https://github.com/spotify/docker-maven-plugin">https://github.com/spotify/docker-maven-plugin</a>)</p>
</li>
</ul>
<p>在这里我们选用@rhuss.在讨论插件前,先来说说在yml配置文件中的Docker Spring profile.在为各种平台构建服务时，这将使我们的工作更加轻松。我们需要配置以下四个属性</p>
<ul>
<li><p>我们使用Spring profile作为docker的识别属性</p>
</li>
<li><p>在内嵌的tomcat中不会有任何端口冲突,因为服务会在他们自己对应的容器中执行.我们可以直接用8080</p>
</li>
<li><p>我们希望使用IP地址在Eureka注册我们的服务。因此，Eureka实例属性preferredIpAddress将设置为true</p>
</li>
<li><p>最后，我们将在serviceUrl：defaultZone中使用Eureka服务器的主机名。</p>
<pre><code>  # For deployment in Docker containers 
  spring: 
      profiles: docker 
  server: 
      port: 8080 
  eureka: 
      instance: 
          preferIpAddress: true 
      client: 
          serviceUrl: 
              defaultZone: http://eureka:8761/eureka/
</code></pre>
</li>
</ul>
<p>使用命令<code>mvn -P docker clean package</code> 将会生成带有tomcat8080内嵌服务的服务jar包并且会使用主机名注册到Eureka服务中</p>
<p>接下来,配置docker-maven-plugin去对我们的微服务构建镜像.这个插件必须首先创建一个Dockerfile.这个dockerfile在两个地方配置.一个是pom.xml文件,一个是docker-assembly.xml文件.如下是pom.xml中插件的配置</p>
<pre><code>&lt;properties&gt; 
&lt;!-- For Docker hub leave empty; use &quot;localhost:5000/&quot; for a local Docker Registry --&gt; 
&lt;docker.registry.name&gt;localhost:5000/&lt;/docker.registry.name&gt; 
&lt;docker.repository.name&gt;$&#123;docker.registry.name&#125;sourabhh/$&#123;project.artifactId&#125;&lt;/docker.repository.name&gt; &lt;/properties&gt;
&lt;plugin&gt; 
    &lt;groupId&gt;org.jolokia&lt;/groupId&gt; 
    &lt;artifactId&gt;docker-maven-plugin&lt;/artifactId&gt; 
    &lt;version&gt;0.13.7&lt;/version&gt; 
    &lt;configuration&gt; 
        &lt;images&gt; 
            &lt;image&gt; 
                &lt;name&gt;$&#123;docker.repository.name&#125;:$&#123;project.version&#125;&lt;/name&gt; 
                &lt;alias&gt;$&#123;project.artifactId&#125;&lt;/alias&gt; 
                &lt;build&gt; 
                    &lt;from&gt;java:8-jre&lt;/from&gt; 
                    &lt;maintainer&gt;sourabhh&lt;/maintainer&gt; 
                    &lt;assembly&gt; 
                        &lt;descriptor&gt;docker-assembly.xml&lt;/descriptor&gt; 
                    &lt;/assembly&gt; 
                    &lt;ports&gt; 
                        &lt;port&gt;8080&lt;/port&gt; 
                    &lt;/ports&gt; 
                    &lt;cmd&gt; 
                        &lt;shell&gt;java -jar \/maven/$&#123;project.build.finalName&#125;.jar server \/maven/docker-config.yml&lt;/shell&gt; 
                    &lt;/cmd&gt; 
                &lt;/build&gt; 
                &lt;run&gt; 
                &lt;!-- To Do --&gt; 
                &lt;/run&gt; 
            &lt;/image&gt; 
        &lt;/images&gt; 
    &lt;/configuration&gt; 
&lt;/plugin&gt;
</code></pre>
<p>创建一个Dockerfile。这将露出端口8080和8081.接下来配置docker-assembly.xml 文件,这文件告诉插件那个文件应该被放入容器中.这文件放置于<small>src&#x2F;main&#x2F;docker</small>目录中</p>
<pre><code>&lt;assembly xmlns=&quot;http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2&quot; xmlns:xsi=&quot;http://www.w3.o xsi:schemaLocation=&quot;http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.or 
    &lt;id&gt;$&#123;project.artifactId&#125;&lt;/id&gt; 
    &lt;files&gt; 
        &lt;file&gt; 
            &lt;source&gt;&#123;basedir&#125;/target/$&#123;project.build.finalName&#125;.jar&lt;/source&gt; 
            &lt;outputDirectory&gt;/&lt;/outputDirectory&gt; 
        &lt;/file&gt; 
        &lt;file&gt; 
            &lt;source&gt;src/main/resources/docker-config.yml&lt;/source&gt; 
            &lt;outputDirectory&gt;/&lt;/outputDirectory&gt; 
        &lt;/file&gt; 
    &lt;/files&gt; 
&lt;/assembly&gt;
</code></pre>
<p>这个文件把服务jar包和docker-config.yml配置文件添加到生成的Dockefile中.这个dockerfile位于<small>target&#x2F;docker</small>.<br>接下来构建docker镜像</p>
<blockquote>
<p>mvn docker:build</p>
</blockquote>
<p>该命令完成后，我们可以使用Docker镜像或运行以下命令来验证本地存储库中的镜像：</p>
<blockquote>
<p>docker run -it -p 8080:8080 sourabhh&#x2F;restaurant-service:PACKT-SNAPSHOT</p>
</blockquote>
<p>使用-it在前台代替-d来执行此命令.</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://zhangbo4881820.gitee.io/myblog">brad 随风起舞</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://zhangbo4881820.gitee.io/myblog/posts/55288/">https://zhangbo4881820.gitee.io/myblog/posts/55288/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://zhangbo4881820.gitee.io/myblog" target="_blank">静待花开</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/myblog/tags/springCloud/">springCloud</a></div><div class="post_share"><div class="social-share" data-image="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/social-share.js/1.0.16/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdnjs.cloudflare.com/ajax/libs/social-share.js/1.0.16/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/myblog/posts/41531/" title="IDEA启动多实例项目"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">IDEA启动多实例项目</div></div></a></div><div class="next-post pull-right"><a href="/myblog/posts/64374/" title="mysqlCookBook03"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">mysqlCookBook03</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/myblog/posts/19354/" title="SpringCloudPractice02"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2019-12-05</div><div class="title">SpringCloudPractice02</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" onerror="this.onerror=null;this.src='/myblog/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">brad 随风起舞</div><div class="author-info__description">有些事情需要一辈子的沉淀</div></div><div class="card-info-data site-data is-center"><a href="/myblog/archives/"><div class="headline">文章</div><div class="length-num">57</div></a><a href="/myblog/tags/"><div class="headline">标签</div><div class="length-num">20</div></a><a href="/myblog/categories/"><div class="headline">分类</div><div class="length-num">25</div></a></div><a id="card-info-btn" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/xxxxx" target="_blank" title="Github"><i class="fab fa-github" style="color: #24292e;"></i></a><a class="social-icon" href="mailto:xxxxxx@gmail.com" target="_blank" title="Email"><i class="fas fa-envelope" style="color: #4a7dbe;"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">This is my Blog</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#springcloud"><span class="toc-number">1.</span> <span class="toc-text">springcloud</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8Netflix-OSS%E5%BE%AE%E6%9C%8D%E5%8A%A1%E7%BB%93%E6%9E%84%E6%A6%82%E8%A6%81"><span class="toc-number">1.1.</span> <span class="toc-text">使用Netflix OSS微服务结构概要</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%8E%E5%8F%91%E7%8E%B0"><span class="toc-number">1.2.</span> <span class="toc-text">服务注册与发现</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%BE%B9%E7%BC%98%E6%9C%8D%E5%8A%A1-Edge-servers"><span class="toc-number">1.3.</span> <span class="toc-text">边缘服务(Edge servers)</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1"><span class="toc-number">1.4.</span> <span class="toc-text">负载均衡</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1"><span class="toc-number">1.4.1.</span> <span class="toc-text">服务端负载均衡</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1"><span class="toc-number">1.4.2.</span> <span class="toc-text">客户端负载均衡</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%96%AD%E8%B7%AF%E5%99%A8-Circuit-breakers"><span class="toc-number">1.5.</span> <span class="toc-text">断路器(Circuit breakers)</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8Hystrix%E7%9A%84%E5%9B%9E%E9%80%80%E6%96%B9%E6%B3%95"><span class="toc-number">1.5.1.</span> <span class="toc-text">使用Hystrix的回退方法</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%9B%91%E6%8E%A7%E5%99%A8-Monitoring"><span class="toc-number">1.6.</span> <span class="toc-text">监控器(Monitoring)</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9B%E5%BB%BAHystrix-dashboard%E6%9C%8D%E5%8A%A1"><span class="toc-number">1.6.1.</span> <span class="toc-text">创建Hystrix dashboard服务</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9B%E5%BB%BANetflix-Turbine%E6%9C%8D%E5%8A%A1"><span class="toc-number">1.6.2.</span> <span class="toc-text">创建Netflix Turbine服务</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8docker%E5%AE%B9%E5%99%A8%E9%83%A8%E7%BD%B2%E5%BE%AE%E6%9C%8D%E5%8A%A1"><span class="toc-number">1.7.</span> <span class="toc-text">使用docker容器部署微服务</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8maven%E6%8F%92%E4%BB%B6%E6%9E%84%E5%BB%BA%E9%95%9C%E5%83%8F"><span class="toc-number">1.7.1.</span> <span class="toc-text">使用maven插件构建镜像</span></a></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/15695/" title="jenkins（1）">jenkins（1）</a><time datetime="2023-09-14T02:17:44.000Z" title="发表于 2023-09-14 10:17:44">2023-09-14</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/22012/" title="wsl的Ubuntu子系统（1）">wsl的Ubuntu子系统（1）</a><time datetime="2023-09-13T08:42:19.000Z" title="发表于 2023-09-13 16:42:19">2023-09-13</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/30302/" title="effective-java-(接口优于抽象类)">effective-java-(接口优于抽象类)</a><time datetime="2023-09-12T08:12:48.000Z" title="发表于 2023-09-12 16:12:48">2023-09-12</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/9473/" title="effective-java(创建和销毁对象--组合的形式优于继承)">effective-java(创建和销毁对象--组合的形式优于继承)</a><time datetime="2023-09-11T08:23:19.000Z" title="发表于 2023-09-11 16:23:19">2023-09-11</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/myblog/posts/16107/" title="Hello World">Hello World</a><time datetime="2023-09-07T09:44:01.910Z" title="发表于 2023-09-07 17:44:01">2023-09-07</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By brad 随风起舞</div><div class="framework-info"><span>框架 </span><a href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/myblog/js/utils.js"></script><script src="/myblog/js/main.js"></script><script src="/myblog/js/fancybox.umd.js"></script><div class="js-pjax"></div><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div class="no-result" id="local-search-results"></div><div id="local-search-stats-wrap"></div></div></div><div id="search-mask"></div><script src="/myblog/js/search/local-search.js"></script></div></div></body></html>